#include <set>

#include "CWebCounter.h"

CWebCounter::CWebCounter()
{
	classtype = WEB;

	//all supported webpage file extension
	file_extension.push_back(".html");
	file_extension.push_back(".htm");
	file_extension.push_back(".shtml");
	file_extension.push_back(".shtm");
	file_extension.push_back(".stm");
	file_extension.push_back(".sht");
	file_extension.push_back(".oth");
	file_extension.push_back(".xhtml");
	file_extension.push_back(".xml");
	file_extension.push_back(".php");
	file_extension.push_back(".asp");
	file_extension.push_back(".aspx");
	file_extension.push_back(".jsp");
	//Changes----
	file_extension.push_back(".cfm");

	//the space to save the separated file
	SourceFileElement tmp;
	tmp.second.file_name = "*.*htm";
	Separation.insert(map<int,SourceFileElement>::value_type(HTML				,tmp));
	tmp.second.file_name = "*.*/htmphp";
	Separation.insert(map<int,SourceFileElement>::value_type(HTML_PHP			,tmp));
	tmp.second.file_name = "*.*/htmjsp";
	Separation.insert(map<int,SourceFileElement>::value_type(HTML_JSP			,tmp));
	tmp.second.file_name = "*.*/htmasp";
	Separation.insert(map<int,SourceFileElement>::value_type(HTML_ASP			,tmp));
	tmp.second.file_name = "*.*jsphp";
	Separation.insert(map<int,SourceFileElement>::value_type(JAVASCRIPT_PHP		,tmp));
	tmp.second.file_name = "*.*jshtm";
	Separation.insert(map<int,SourceFileElement>::value_type(JAVASCRIPT_HTML	,tmp));
	tmp.second.file_name = "*.*jsjsp";
	Separation.insert(map<int,SourceFileElement>::value_type(JAVASCRIPT_JSP		,tmp));
	tmp.second.file_name = "*.*jsasps";
	Separation.insert(map<int,SourceFileElement>::value_type(JAVASCRIPT_ASP_S	,tmp));
	tmp.second.file_name = "*.*jsaspc";
	Separation.insert(map<int,SourceFileElement>::value_type(JAVASCRIPT_ASP_C	,tmp));
	tmp.second.file_name = "*.*php";
	Separation.insert(map<int,SourceFileElement>::value_type(PHP				,tmp));
	tmp.second.file_name = "*.*java";
	Separation.insert(map<int,SourceFileElement>::value_type(JAVA_JSP			,tmp));
	tmp.second.file_name = "*.*cs";
	Separation.insert(map<int,SourceFileElement>::value_type(CSHARP_HTML		,tmp));
	tmp.second.file_name = "*.*vbsphp";
	Separation.insert(map<int,SourceFileElement>::value_type(VBS_PHP			,tmp));
	tmp.second.file_name = "*.*vbshtm";
	Separation.insert(map<int,SourceFileElement>::value_type(VBS_HTML			,tmp));
	tmp.second.file_name = "*.*vbsjsp";
	Separation.insert(map<int,SourceFileElement>::value_type(VBS_JSP			,tmp));
	tmp.second.file_name = "*.*vbsasps";
	Separation.insert(map<int,SourceFileElement>::value_type(VBS_ASP_S			,tmp));
	tmp.second.file_name = "*.*vbsaspc";
	Separation.insert(map<int,SourceFileElement>::value_type(VBS_ASP_C			,tmp));
	tmp.second.file_name = "*.*csasps";
	Separation.insert(map<int,SourceFileElement>::value_type(CS_ASP_S	,tmp));
	//changes---
	tmp.second.file_name = "*.*cfm";
	Separation.insert(map<int,SourceFileElement>::value_type(COLDFUSION ,tmp )); 

	//the space to save the separated line
	lineElement tmp2;
	SeparatedLine.insert(map<int,lineElement>::value_type(HTML					,tmp2));
	SeparatedLine.insert(map<int,lineElement>::value_type(HTML_PHP				,tmp2));
	SeparatedLine.insert(map<int,lineElement>::value_type(HTML_JSP				,tmp2));
	SeparatedLine.insert(map<int,lineElement>::value_type(HTML_ASP				,tmp2));
	SeparatedLine.insert(map<int,lineElement>::value_type(JAVASCRIPT_PHP		,tmp2));
	SeparatedLine.insert(map<int,lineElement>::value_type(JAVASCRIPT_HTML		,tmp2));
	SeparatedLine.insert(map<int,lineElement>::value_type(JAVASCRIPT_JSP		,tmp2));
	SeparatedLine.insert(map<int,lineElement>::value_type(JAVASCRIPT_ASP_S		,tmp2));
	SeparatedLine.insert(map<int,lineElement>::value_type(JAVASCRIPT_ASP_C		,tmp2));
	SeparatedLine.insert(map<int,lineElement>::value_type(PHP					,tmp2));
	SeparatedLine.insert(map<int,lineElement>::value_type(JAVA_JSP				,tmp2));
	SeparatedLine.insert(map<int,lineElement>::value_type(CSHARP_HTML			,tmp2));
	SeparatedLine.insert(map<int,lineElement>::value_type(VBS_PHP				,tmp2));
	SeparatedLine.insert(map<int,lineElement>::value_type(VBS_HTML				,tmp2));
	SeparatedLine.insert(map<int,lineElement>::value_type(VBS_JSP				,tmp2));
	SeparatedLine.insert(map<int,lineElement>::value_type(VBS_ASP_S				,tmp2));
	SeparatedLine.insert(map<int,lineElement>::value_type(VBS_ASP_C				,tmp2));
	SeparatedLine.insert(map<int,lineElement>::value_type(CS_ASP_S				,tmp2));
	//Changes--
	SeparatedLine.insert(map<int,lineElement>::value_type(COLDFUSION			,tmp2)); 
	//supported script tag
	//this container contains the tags that may appear in html code to specify the script language
	TagTable.insert(map<string,int>::value_type(string("<?php")			,WEB_PHP_START));
	TagTable.insert(map<string,int>::value_type(string("<?")			,WEB_PHP_START2)); 
	TagTable.insert(map<string,int>::value_type(string("?>")			,WEB_PHP_END));
	TagTable.insert(map<string,int>::value_type(string("<script")		,WEB_SCRIPT_START));
	TagTable.insert(map<string,int>::value_type(string("</script")		,WEB_SCRIPT_END));
	TagTable.insert(map<string,int>::value_type(string("<%")			,WEB_ASP_JSP_START));
	TagTable.insert(map<string,int>::value_type(string("%>")			,WEB_ASP_JSP_END));

	//supported script language
	//this container contains the strings that may appear in <script language="******">
	ScriptTable.insert(map<string,int>::value_type(string("javascript")	,JAVASCRIPT));
	ScriptTable.insert(map<string,int>::value_type(string("jscript")	,JAVASCRIPT));
	ScriptTable.insert(map<string,int>::value_type(string("c#")			,CSHARP_HTML));
	ScriptTable.insert(map<string,int>::value_type(string("php")		,PHP));
	ScriptTable.insert(map<string,int>::value_type(string("vb")			,VB));

}

void CWebCounter::CleanSeparatedFile()
{
	for (map<int,SourceFileElement>::iterator iter = Separation.begin(); iter != Separation.end(); iter++)
	{
		iter->second.first.clear();
	}
	while (!PreviousLanguage.empty())
	{
		PreviousLanguage.pop();
	}
}

void CWebCounter::CleanSeparatedLine()
{
	for (map<int,lineElement>::iterator iter = SeparatedLine.begin(); iter != SeparatedLine.end(); iter++)
	{
		iter->second.line = "";
		iter->second.lineNumber = 0;
	}
}

// Method: CountSLOC(...)
// Description: Separate web files into different languages and count these 
//              languages using their corresponding counters. 
// -----------------------------------------
// Revision  Date     Author  Modification
// Rev-1     Fall 07  WHH     Original author
// Rev-2     8/8/08   VN      Modifications:
//							  - Supported .aspx file
//							  - Supported <script runat="server" language="??"
//							  - Detected csharp as a language at server side
//							  - Fixed defects with case <td width=7%>, 
//                                %> was detected as ending script type
//							  - Fixed %>, script> without the corresponding begin tags
//Rev-3		4/19/09   VN	  Mod041909VN: name the newly created file with its embedding file's filename.
//								The filename is used in the matching function for the comparison purpose
//								new file's filename = embedding file's filename + its default filename
//								(note: the filename contains *.*/ in its name)
//Rev-4     4/29/09   VN	  Mod042909AS: Included warning fixes provided by Aaron Shumate of NGC
int CWebCounter::CountSLOC(filemap* fmap, results* result)
{
	CleanSeparatedFile();
	lineElement current_line;
	lineElement separate_line;
	size_t pos;
	int current_language;
	bool blank_line;
	bool first_line_of_asp = true;
	int script_language_of_asp = VBS_ASP_S;

	// var used to identify <?xml
	bool xml_specified = false;

	// Code used for writing to files
	size_t preLang = INVALID_POSITION; //Mod042905AS
	// int tempi;

	// End Code used for...

	// Supported aspx, VN
	bool cs_inline = false;
	string tempstr;
	// End Supported aspx
	// Mod041909VN
	size_t tempi;

	string file_ext = result->file_name.substr(result->file_name.find_last_of("."));
	file_ext = CUtil::to_lower(file_ext);
	if (file_ext == ".asp") // what about .aspx?
	{
		current_language = HTML_ASP;
		language_name = "ASP";
	}
	else if (file_ext == ".jsp")
	{
		current_language = HTML_JSP;
		language_name = "JSP";
	}
	else if (file_ext == ".php")
	{
		current_language = HTML_PHP;
	}else if(file_ext == ".cfm")
	{
		current_language = COLDFUSION;
		language_name = "CFM";
	}
	else
	{
		current_language = HTML;
	}


	size_t search_result;
	for (filemap::iterator iter = fmap->begin(); iter != fmap->end(); iter++)
	{
		current_line.line = iter->line;
		current_line.lineNumber = iter->lineNumber;

		if (current_line.line.length() == 0)
		{
			blank_line = true;
		}
		else
		{
			blank_line = false;
		}
		while (current_line.line.length() > 0)
		{
			search_result = CUtil::FindStringsCaseInsensitive(current_line.line,TagTable,pos, preLang);
			if (search_result != INVALID_POSITION && !xml_specified) //Mod042909
			{
				tempstr = CUtil::TrimString(CUtil::to_lower(current_line.line));
				if (tempstr.find("<?xml") == 0 && tempstr.find("version") != string::npos)
				{
					search_result = INVALID_POSITION; //Mod042909
					xml_specified = true;
				}
			}

			// Writing to separated files starts here
			if (search_result != INVALID_POSITION) preLang = search_result; //Mod042909
			// End Writing to ....

			switch (search_result)
			{
			case WEB_PHP_START:
				{
					SeparatedLine[current_language].line += current_line.line.substr(0,pos+5);
					PreviousLanguage.push(current_language);
					current_language = PHP;
					current_line.line = current_line.line.substr(pos+5);
				}
				break;
			case WEB_PHP_START2:
				{
					SeparatedLine[current_language].line += current_line.line.substr(0,pos+2);
					PreviousLanguage.push(current_language);
					current_language = PHP;
					current_line.line = current_line.line.substr(pos+2);
				}
				break;
			case WEB_PHP_END:
			case WEB_ASP_JSP_END:
				{
					SeparatedLine[current_language].line += current_line.line.substr(0,pos);
					if (current_language == JAVA_JSP || current_language == CS_ASP_S)
					{
						//special process for JSP; add semicolon at the end of line if there is no semicolon
						SeparatedLine[current_language].line = CUtil::TrimString(SeparatedLine[current_language].line);
						size_t lng = SeparatedLine[current_language].line.length();
						if (lng>0 && SeparatedLine[current_language].line[lng-1] != ';') SeparatedLine[current_language].line += ";";
					}
					current_language = PreviousLanguage.top();
					PreviousLanguage.pop();
					// VN: Remove %>, next line
					//SeparatedLine[current_language].line += current_line.line.substr(pos,2);
					current_line.line = current_line.line.substr(pos+2);
				}
				break;
			case WEB_SCRIPT_START:
				{
					size_t close_pos = current_line.line.find_first_of(">",pos+7);
					SeparatedLine[current_language].line += current_line.line.substr(0,close_pos+1);
					string language = CUtil::to_lower(current_line.line.substr(pos+7,close_pos-pos+7));					
					if(close_pos < current_line.line.size())
					{
						current_line.line = current_line.line.substr(close_pos+1);
					}
					else
					{
						current_line.line = "";
					}
					PreviousLanguage.push(current_language);

					current_language = (int)CUtil::FindStringsCaseInsensitive(language,ScriptTable,pos);	

					// check for <script runat="server"
					// added by VN, 8/7/08
					if (language.find("runat") != string::npos 
						&& language.find("server") != string::npos) {
						switch(current_language) 
						{
						//case -1: // assign them to languages but also using aspx_lang
							//script_language_of_asp = JAVASCRIPT_ASP_S;
						case JAVASCRIPT: 
							script_language_of_asp = JAVASCRIPT_ASP_S;
							break;
						case VB:
							script_language_of_asp = VBS_ASP_S;
							break;
						case CSHARP_HTML:
							script_language_of_asp = CS_ASP_S;
							break;
						//default: 
						}
						current_language = script_language_of_asp;
					} 
					else // the script is run at client
					switch (current_language)
					{
					case JAVASCRIPT:
					case -1:
						if (file_ext == ".asp" || file_ext == ".aspx") // added .aspx, VN 8/8/08
						{
							 current_language = JAVASCRIPT_ASP_C;
						}
						else if (file_ext == ".jsp")
						{
							current_language = JAVASCRIPT_JSP;
						}
						else if (file_ext == ".php")
						{
							current_language = JAVASCRIPT_PHP;
						}
						else
						{
							current_language = JAVASCRIPT_HTML;
						}
						break;
					case CSHARP_HTML:
					case PHP:
						break;
					case VB:
						if (file_ext == ".asp" || file_ext == ".aspx") // added .aspx, VN 8/8/08
						{
							 current_language = VBS_ASP_C;
						}
						else if (file_ext == ".jsp")
						{
							current_language = VBS_JSP;
						}
						else if (file_ext == ".php")
						{
							current_language = VBS_PHP;
						}
						else
						{
							current_language = VBS_HTML;
						}
						break;
					}
				}
				break;
			case WEB_SCRIPT_END:
				{
					SeparatedLine[current_language].line += current_line.line.substr(0,pos);
					// VN: Fixed an error when </script> appears w/o <script>, next two lines					
					if (PreviousLanguage.empty()) 
						current_line.line = current_line.line.substr(pos + 9);
					else {
						current_language = PreviousLanguage.top();
						PreviousLanguage.pop();
						size_t close_pos = current_line.line.find_first_of(">",pos+8);
						SeparatedLine[current_language].line += current_line.line.substr(pos,close_pos-pos+1);
						if(close_pos < current_line.line.size())
						{
							current_line.line = current_line.line.substr(close_pos+1);
						}
						else
						{
							current_line.line = "";
						}
					}
				}
				break;
			case WEB_ASP_JSP_START:
				{
					// VN: Removed <% from HTML page, next line, changed pos + 2 to pos
					SeparatedLine[current_language].line += current_line.line.substr(0,pos);
					PreviousLanguage.push(current_language);
					if(file_ext == ".asp" || file_ext == ".aspx")
					{
						if(first_line_of_asp)
						{
							// The following lines within this "if" are used to 
							// check the language used in the page
							// case: <%@ Page language="???">
							tempstr = CUtil::to_lower(current_line.line);
							if (tempstr.find("@") != string::npos) 
							{
								if ((tempstr.find("javascript") != string::npos)
									|| (tempstr.find("js") != string::npos))
								{
									script_language_of_asp = JAVASCRIPT_ASP_S;
								}
								else if ((tempstr.find("c#") != string::npos)
									|| (tempstr.find("cs") != string::npos)
									|| (tempstr.find("csharp") != string::npos)) 
								{
									script_language_of_asp = CS_ASP_S;
								}
							}
							first_line_of_asp = false;
						}
						current_language = script_language_of_asp;
					}
					else if (file_ext == ".jsp")
					{
						current_language = JAVA_JSP;
					}
					else
					{
						current_language = VBS_ASP_S;
					}
					current_line.line = current_line.line.substr(pos+2);
				}
				break;
			default:
				{
					SeparatedLine[current_language].line += current_line.line;
					SeparatedLine[current_language].lineNumber = current_line.lineNumber;
					current_line.line = "";
				}
				break;
			}//switch
		}//while
		
		//write into Separation
		if(blank_line)
		{
			Separation[current_language].first.push_back(current_line);
		}
		else
		{			
			for(map<int,lineElement>::iterator i = SeparatedLine.begin(); i != SeparatedLine.end(); i++)
			{
				if(i->second.line.length()>0)
				{
					Separation[i->first].first.push_back(i->second);

					//Mod041909VN: name the newly created file with its embedding file's filename.
					//  The filename is used in the matching function for the comparison purpose
					//	new file's filename = embedding file's filename + its default filename
					//  (note: the filename contains *.*/ in its name)
					if (Separation[i->first].second.file_name.find(result->file_name) != 0) 
					{
						tempi = Separation[i->first].second.file_name.find("*.*");
						Separation[i->first].second.file_name = result->file_name 
								+ Separation[i->first].second.file_name.substr(tempi);
					}					
				}
			}
		}
		CleanSeparatedLine();

	}//for

	return 1;
}

ofstream* CWebCounter::getOutputStream(string fname, string outputFileNamePrePend)
{
	if (fname == "php")
	{
		if (!php_output_file.is_open())
		{
			string ofname = outputFileNamePrePend + "PHP";
			ofname += output_file_name;
			php_output_file.open(ofname.c_str(),ios::out);

			if (!php_output_file.is_open()) return NULL;
			CUtil::PrintFileHeader(php_output_file, "SLOC COUNT RESULTS FOR PHP FILES");

			php_output_file<<"                     |   HTML     |  JS-Clnt    |  VBS-Clnt   |       PHP           |"<<endl;
			php_output_file<<" Blank |  Comments   | Data Exec. | Data  Exec. | Data  Exec. | Comp.   Data  Exec. |            Logical SLOC             | File  Module"<<endl;
			php_output_file<<" Lines | Whole Embed | Decl Instr | Decl  Instr | Decl  Instr | Direct. Decl  Instr |  Total   HTML JS-Clnt VBS-Clnt  PHP | Type   Name"<<endl;
			php_output_file<<"-------+-------------+------------+-------------+-------------+---------------------+--------+----------------------------+------------------"<<endl;
		}
		return &php_output_file;
	}
	else if (fname == "jsp")
	{
		if (!jsp_output_file.is_open())
		{
			string ofname = outputFileNamePrePend + "JSP";
			ofname += output_file_name;
			jsp_output_file.open(ofname.c_str(),ios::out);

			if (!jsp_output_file.is_open()) return NULL;

			CUtil::PrintFileHeader(jsp_output_file, "SLOC COUNT RESULTS FOR JSP FILES");

			jsp_output_file<<"                     |   HTML     |  JS-Clnt    |  VBS-Clnt   |       Java          |"<<endl;
			jsp_output_file<<" Blank |  Comments   | Word Exec. | Data  Exec. | Data  Exec. | Comp.   Data  Exec. |            Logical SLOC             | File  Module"<<endl;
			jsp_output_file<<" Lines | Whole Embed | LOC  Instr | Decl  Instr | Decl  Instr | Direct. Decl  Instr |  Total   HTML JS-Clnt VBS-Clnt Java | Type   Name"<<endl;
			jsp_output_file<<"-------+-------------+------------+-------------+-------------+---------------------+--------+----------------------------+------------------"<<endl;

		}
		return &jsp_output_file;
	}
	else if (fname == "asp")
	{
		if (!asp_output_file.is_open())
		{
			string ofname = outputFileNamePrePend + "ASP";
			ofname += output_file_name;
			asp_output_file.open(ofname.c_str(),ios::out);

			if (!asp_output_file.is_open()) return NULL;

			CUtil::PrintFileHeader(asp_output_file, "SLOC COUNT RESULTS FOR ASP FILES");

			asp_output_file<<"                     |   HTML     |  JS-Clnt    |  VBS-Clnt   |  JS-Svr     |  VBS-Svr    |"<<endl;
			asp_output_file<<" Blank |  Comments   | Word Exec. | Data  Exec. | Data  Exec. | Data  Exec. | Data  Exec. |                 Logical SLOC                   | File  Module"<<endl;
			asp_output_file<<" Lines | Whole Embed | LOC  Instr | Decl  Instr | Decl  Instr | Decl  Instr | Decl  Instr |  Total    HTML JS-Clnt VBS-Clnt JS-Svr VBS-Svr | Type   Name"<<endl;
			asp_output_file<<"-------+-------------+------------+-------------+-------------+-------------+-------------+--------+---------------------------------------+------------------"<<endl;
		}
		return &asp_output_file;
	}
	else //html
	{
		if (!html_output_file.is_open())
		{
			string ofname = outputFileNamePrePend + "HTML";
			ofname += output_file_name;
			html_output_file.open(ofname.c_str(),ios::out);

			if (!html_output_file.is_open()) return NULL;

			CUtil::PrintFileHeader(html_output_file, "SLOC COUNT RESULTS FOR HTML FILES");

			html_output_file<<"                     |   HTML     |  JS-Clnt    |  VBS-Clnt   |  C#-Clnt    |"<<endl;
			html_output_file<<" Blank |  Comments   | Word Exec. | Data  Exec. | Data  Exec. | Data  Exec. |                Logical SLOC            | File  Module"<<endl;
			html_output_file<<" Lines | Whole Embed | LOC  Instr | Decl  Instr | Decl  Instr | Decl  Instr |  Total   HTML JS-Clnt VBS-Clnt C#-Clnt | Type   Name"<<endl;
			html_output_file<<"-------+-------------+------------+-------------+-------------+-------------+--------+-------------------------------+------------------"<<endl;
		}
		return &html_output_file;
	}
}

void CWebCounter::closeOutputStream()
{
	if (php_output_file.is_open())
	{
		php_output_file.close();
	}
	if (jsp_output_file.is_open())
	{
		jsp_output_file.close();
	}
	if (asp_output_file.is_open())
	{
		asp_output_file.close();
	}
	if (html_output_file.is_open())
	{
		html_output_file.close();
	}
}
